﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace HobSoft.SoccerBet2014.Domain.Implementation
{
    using Core;
    using Models.Core;
    using SoccerBet2014.Common.Queryable;

    public abstract class ReadWriteBinaryRepository<T, K> : ReadonlyBinaryRepository<T, K>, IReadWriteRepository<T>, IUniKeyReadWriteRepository<T>
                where T : class, IModel
    {

        private object _syncLock = new object();
        public virtual void Submit()
        {
            BinaryContext.Current.Submit<T, K>();
        }

        public virtual T AddOrUpdate(T model)
        {
            if (model == null) return null;

            var key = FindKey(model);
            BinModels.AddOrUpdate(key, model, (k, t) => model);
            return model;
        }

        public virtual int AddOrUpdate(IList<T> models)
        {
            if (models == null)
                throw new ArgumentNullException("models", "need models.");
            if (!models.Any())
                return 0;

            foreach (var mod in models)
            {
                var key = FindKey(mod);
                BinModels.AddOrUpdate(key, mod, (k, t) => mod);
            }
            return models.Count;
        }

        public virtual bool Delete(T model)
        {
            if (model == null) return false;

            var key = FindKey(model);
            T del;
            return BinModels.TryRemove(key, out del);
        }

        public virtual bool Delete(params object[] id)
        {
            if (id.Length == 0) return false;
            K key = (K)id[0];
            T del;
            return BinModels.TryRemove(key, out del);
        }

        public virtual int DeleteMany(Expression<Func<T, bool>> predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate", "need predicate.");

            var delQuery = BinModels.Values.AsQueryable().Where(predicate).Select(m => FindKey(m));
            int count = delQuery.Count();
            foreach (var id in delQuery)
            {
                T del;
                BinModels.TryRemove(id, out del);
            }
            return count;
        }

        public virtual int Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updateExpression)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate", "need predicate.");
            if (updateExpression == null)
                throw new ArgumentNullException("updateExpression", "need updateExpression.");

            var updateQuery = BinModels.Values.AsQueryable().Where(predicate);
            int count = updateQuery.Count();
            updateQuery.Update(updateExpression);
            return count;
        }

        public virtual int DeleteMany(params object[] keys)
        {
            if (keys == null)
                throw new ArgumentNullException("keys", "need keys");
            if (!keys.Any()) return 0;

            foreach (K key in keys)
            {
                T del;
                BinModels.TryRemove(key, out del);
            }
            return keys.Length;
        }

        public virtual int Update(Expression<Func<T, T>> updateExpression, params object[] keys)
        {
            if (keys == null)
                throw new ArgumentNullException("keys", "need keys");
            if (updateExpression == null)
                throw new ArgumentNullException("updateExpression", "need updateExpression");
            if (!keys.Any()) return 0;
            var query = from m in BinModels
                        join k in keys on m.Key equals k
                        select m.Value;
            query.AsQueryable().Update(updateExpression);
            return keys.Length;
        }
    }
}
